# -*- coding: utf-8 -*-
# Author: xiantingDeng
# File: run.py
# Time: 11:10

import os
from multiprocessing import Pool

import jprops
import pytest

from bin.config.initialize import InitializeDevice
from bin.util.file_util import FilePath
from bin.util.log_util import LogGing
from bin.util.shell_util import Shell
from bin.util.tool_util import ls_by_key


class Run:

    def __init__(self):
        self.__log = LogGing().logger()
        self.file = FilePath()
        self.ini = InitializeDevice()
        self.env = self.ini.obtain_device_information()

        # 定义测试报告路径
        self.xml_report_path = self.file.get_type_time_file("report","xml")
        self.html_report_path = self.file.get_type_time_file("report","html")
        self.properties_path = os.path.join(self.xml_report_path, 'environment.properties')

    def get_run_args(self):

        # 配置用于输出到报告的日志格式
        args = ['-s', '-q', '--alluredir', self.xml_report_path, "--verbose"]
        return args

    def generate_report(self):

        # 给报告中添加执行环境信息
        appx = self.ini.obtain_app_information()
        env_properties = {}
        for k,v in self.env[0].items():
            env_properties.update({k:v})
        env_properties.update(appx)
        try:
            with open(self.properties_path, 'w', encoding='utf-8') as fp:
                jprops.store_properties(fp, env_properties)
        except Exception:
            self.__log.error('配置环境未输出到报告中')

        # 执行生成报告命令
        cmd = 'allure generate %s -o %s --clean' % (self.xml_report_path, self.html_report_path)
        try:
            Shell.invoke(cmd)
            self.__log.info("测试报告成功生成")
        except Exception:
            self.__log.error("Html测试报告生成失败,确保已经安装了Allure-Commandline")

    def _exec_pytest(self, sys_argv):
        # 读取命令行参数，单设备执行
        args = sys_argv + self.get_run_args()

        # 清除log对象已有的handler
        # for handle in log.handlers:
        #     log.removeHandler(handle)
        #
        # # 定义handler的输出格式
        # formatter = logging.Formatter('%(levelname)s %(message)s'.format(os.getpid()), "%H:%M:%S")
        #
        # # 添加用于输出到控制台的handler
        # console_handler = Logbuilder().get_consolehandler(formatter)
        # log.addHandler(console_handler)

        current_device = self.ini.obtain_device_information()[0]
        print(current_device)
        # 当前进程的环境对象通过回调写入current_device
        self.ini.callback_current_device(current_device)
        pytest.main(args)

    @staticmethod
    def _batch_exec_pytest(current_device, testsuite_paths):
        # 通过多进程调度执行用例（多台设备）
        # testsuite_paths 为list ，格式为[tests/test_suit2,tests/test_suit3]

        Run().__log.info('Run task pid={}  testsuite_paths: {}...'.format(os.getpid(), testsuite_paths))

        # 清除log对象已有的handler
        # for handle in log.handlers:
        #     log.removeHandler(handle)
        #
        # # 定义handler的输出格式
        # formatter = logging.Formatter('pid:{} %(levelname)s %(message)s'.format(os.getpid()), "%H:%M:%S")
        #
        # # 添加用于输出到控制台的handler
        # console_handler = Logbuilder().get_consolehandler(formatter)
        #
        # log.addHandler(console_handler)

        # 当前进程的环境对象通过回调写入current_device
        Run().ini.callback_current_device(current_device)

        # 导入参数执行pytest
        args = testsuite_paths + Run().get_run_args()
        print(args)
        # pytest.main(args)

    def _scheduling_process(self):
        # 给多台设备分配测试任务的调度算法（简单平均调度）
        # 其中suit1是文件夹的名字、device1是配置中device设备的key

        """
        :return:
        {
        device1:[suit1,suit2]
        device2:[suit3,suit4]
        device3:[suit5]
        }

        """

        # tests_path=self.env.path.get('tests')
        path = self.file.get_crm_path("testcase")
        suitname_list = ls_by_key(path, 'test_')
        device = self.ini.obtain_device_information()

        if len(suitname_list) == 0 or len(device) == 0:
            raise IndexError('data is bad')

        scheduling_info = {}
        for i in device:
            scheduling_info[i["deviceName"]] = suitname_list
        # task_num,mod =divmod(len(suitname_list),len(self.devices)) # 取商和余数

        # suitname_list_slice=[]
        # tmp=[]
        # count=0
        # for suitname in suitname_list:
        #     tmp.append(suitname)
        #     if count<(len(suitname_list)-mod):
        #         if len(tmp)==task_num:
        #             suitname_list_slice.append(tmp)
        #             tmp=[]
        #     count+=1
        # print(suitname_list_slice)
        #
        # #对最后一个加入整除后剩余的
        # last_ele=suitname_list_slice[-1]
        # suitname_list_slice[-1]=last_ele+tmp
        #
        #
        # scheduling_info={}
        # count=0
        # for device_key in self.devices:
        #     scheduling_info[device_key]=suitname_list_slice[count]
        #     count=count+1

        self.__log.info("多设备分配的测试集执行为: {}\n".format(str(scheduling_info)))
        return scheduling_info

    def exec(self, sys_argv):

        if len(sys_argv) != 0:
            self._exec_pytest(sys_argv)
        else:
            dir_tests = self.file.get_crm_path("testcase")
            scheduling_info = self._scheduling_process()
            pool = Pool(len(scheduling_info))  # 根据设备数进行进程池数量
            testsuite_paths = []
            for suit in scheduling_info.values():
                for i in suit:
                    testsuite_paths.append(os.path.join(dir_tests, i))  # 把test目录使用append加入到testsuite_paths中
                break
            for device in self.env:
                try:
                    pool.apply_async(self._batch_exec_pytest, args=(device, testsuite_paths))
                except Exception:
                    raise ChildProcessError
            pool.close()
            pool.join()


if __name__ == '__main__':
    print(Run().exec([]))
    # print(Run()._scheduling_process())
